#define CCW_TAG "HDMI"
#include "ccw_hdmi.h"
#include "log.h"
#include "delay.h"

#define PRINT_SCDC 1

static uint8_t hdmi_sink_status = 0;


unsigned char need_check[] = {0x02, 0x03, 0x10, 0x11, 0x20, 0x21, 0x30, 0x40, 0x41, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0xD0, 0xD1, 0xD2};

static char show_scdc_data(char check)
{
	int i = 0;
	char ret = 0;
	unsigned char scdc_reg_data[300] = {0x00};
	
    #if 1
    for (i = 0;i < 15; i++){
        ret = IIC_Read_Bytes(0xA8, i << 4, 16, scdc_reg_data + (i << 4));
    }
    if (check){
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x01] Sink Version: 0x%02x\r\n", scdc_reg_data[0x01]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x02] Source Version: 0x%02x\r\n", scdc_reg_data[0x02]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x03] Unknow Register: 0x%02x\r\n", scdc_reg_data[0x03]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x10] Update_0: 0x%02x\r\n", scdc_reg_data[0x10]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x11] Update_1: 0x%02x\r\n", scdc_reg_data[0x11]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x20] TMDS_Config: 0x%02x\r\n", scdc_reg_data[0x20]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x21] Scrambler_Status: 0x%02x\r\n", scdc_reg_data[0x21]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x30] Config_0: 0x%02x\r\n", scdc_reg_data[0x30]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x40] Status_Flags_0: 0x%02x\r\n", scdc_reg_data[0x40]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x41] Status_Flags_1: 0x%02x\r\n", scdc_reg_data[0x41]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x50~0x51] Err_Det_0: 0x%02x%02x\r\n", scdc_reg_data[0x51], scdc_reg_data[0x50]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x52~0x53] Err_Det_1: 0x%02x%02x\r\n", scdc_reg_data[0x53], scdc_reg_data[0x52]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x54~0x55] Err_Det_2: 0x%02x%02x\r\n", scdc_reg_data[0x55], scdc_reg_data[0x54]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0x56] Err_Det_Checksum: 0x%02x\r\n", scdc_reg_data[0x56]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xC0] Test_Config_0: 0x%02x\r\n", scdc_reg_data[0xC0]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD2] IEEE OUI, First Octet: 0x%02x\r\n", scdc_reg_data[0xD2]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD1] IEEE OUI, Second Octet: 0x%02x\r\n", scdc_reg_data[0xD1]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD0] IEEE OUI, Third Octet: 0x%02x\r\n", scdc_reg_data[0xD0]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD3~0xDA] Device ID: %s\r\n", &scdc_reg_data[0xD3]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDB 4H] Device ID, Hardware_Major_Rev: 0x%02x\r\n", scdc_reg_data[0xDB] >> 4);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDB 4L] Device ID, Hardware_Minor_Rev: 0x%02x\r\n", scdc_reg_data[0xDB] & 0x0F);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDC] Device ID, Software_Major_Rev: 0x%02x\r\n", scdc_reg_data[0xDC]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDD] Device ID, Software_Minor_Rev: 0x%02x\r\n", scdc_reg_data[0xDD]);
        ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDE~0xFF] Manufacturer Specific: \r\n");
        for (i = 0; i < 18; i++) {
            ccw_dbg_log_d(CCW_TAG, "      [0x%02X]: 0x%02x\r\n", 0xDE + i, scdc_reg_data[0xDE + i]);
        }
        ccw_dbg_log_d(CCW_TAG, "\r\n");
        ccw_dbg_log_d(CCW_TAG, "-----------------------------------------------------------------------\r\n");
        for (i = 0; i < sizeof(need_check); i++){
            if (scdc_reg_data[need_check[i]] != 0x00){
                ret = scdc_reg_data[need_check[i]] != 0x00;
                ccw_dbg_log_d(CCW_TAG, "[SCDC] Error: [0x%02X].[0x%02x != 0x%02x]\r\n", need_check[i], scdc_reg_data[need_check[i]], 0x00);
            }
        }
        ccw_dbg_log_d(CCW_TAG, "-----------------------------------------------------------------------\r\n");
    }

    #else



	scdc_reg_data[0x01] = IIC_Read_One_Byte(0xA8, 0x01);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x01] Sink Version: 0x%02x\r\n", scdc_reg_data[0x01]);

	scdc_reg_data[0x02] = IIC_Read_One_Byte(0xA8, 0x02);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x02] Source Version: 0x%02x\r\n", scdc_reg_data[0x02]);

	scdc_reg_data[0x03] = IIC_Read_One_Byte(0xA8, 0x03);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x03] Unknow Register: 0x%02x\r\n", scdc_reg_data[0x03]);

	scdc_reg_data[0x10] = IIC_Read_One_Byte(0xA8, 0x10);
	scdc_reg_data[0x11] = IIC_Read_One_Byte(0xA8, 0x11);
	if (check){
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x10] Update_0: 0x%02x\r\n", scdc_reg_data[0x10]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x11] Update_1: 0x%02x\r\n", scdc_reg_data[0x11]);
	}

	scdc_reg_data[0x20] = IIC_Read_One_Byte(0xA8, 0x20);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x20] TMDS_Config: 0x%02x\r\n", scdc_reg_data[0x20]);

	scdc_reg_data[0x21] = IIC_Read_One_Byte(0xA8, 0x21);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x21] Scrambler_Status: 0x%02x\r\n", scdc_reg_data[0x21]);

	scdc_reg_data[0x30] = IIC_Read_One_Byte(0xA8, 0x30);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x30] Config_0: 0x%02x\r\n", scdc_reg_data[0x30]);

	scdc_reg_data[0x40] = IIC_Read_One_Byte(0xA8, 0x40);
	scdc_reg_data[0x41] = IIC_Read_One_Byte(0xA8, 0x41);
	if (check){
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x40] Status_Flags_0: 0x%02x\r\n", scdc_reg_data[0x40]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x41] Status_Flags_1: 0x%02x\r\n", scdc_reg_data[0x41]);
	}

	scdc_reg_data[0x50] = IIC_Read_One_Byte(0xA8, 0x50);
	scdc_reg_data[0x52] = IIC_Read_One_Byte(0xA8, 0x52);
	scdc_reg_data[0x54] = IIC_Read_One_Byte(0xA8, 0x54);
	if (check){
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x50~0x51] Err_Det_0: 0x%02x%02x\r\n", scdc_reg_data[0x51], scdc_reg_data[0x50]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x52~0x53] Err_Det_1: 0x%02x%02x\r\n", scdc_reg_data[0x53], scdc_reg_data[0x52]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x54~0x55] Err_Det_2: 0x%02x%02x\r\n", scdc_reg_data[0x55], scdc_reg_data[0x54]);
	}
	
	scdc_reg_data[0x56] = IIC_Read_One_Byte(0xA8, 0x56);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0x56] Err_Det_Checksum: 0x%02x\r\n", scdc_reg_data[0x56]);

	scdc_reg_data[0xC0] = IIC_Read_One_Byte(0xA8, 0xC0);
	if (check)
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xC0] Test_Config_0: 0x%02x\r\n", scdc_reg_data[0xC0]);
	
	scdc_reg_data[0xD2] = IIC_Read_One_Byte(0xA8, 0xD2);
	scdc_reg_data[0xD1] = IIC_Read_One_Byte(0xA8, 0xD1);
	scdc_reg_data[0xD0] = IIC_Read_One_Byte(0xA8, 0xD0);
	if (check) {
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD2] IEEE OUI, First Octet: 0x%02x\r\n", scdc_reg_data[0xD2]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD1] IEEE OUI, Second Octet: 0x%02x\r\n", scdc_reg_data[0xD1]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD0] IEEE OUI, Third Octet: 0x%02x\r\n", scdc_reg_data[0xD0]);
	}
	scdc_reg_data[0xD3] = IIC_Read_One_Byte(0xA8, 0xD3);
	scdc_reg_data[0xD4] = IIC_Read_One_Byte(0xA8, 0xD4);
	scdc_reg_data[0xD5] = IIC_Read_One_Byte(0xA8, 0xD5);
	scdc_reg_data[0xD6] = IIC_Read_One_Byte(0xA8, 0xD6);
	scdc_reg_data[0xD7] = IIC_Read_One_Byte(0xA8, 0xD7);
	scdc_reg_data[0xD8] = IIC_Read_One_Byte(0xA8, 0xD8);
	scdc_reg_data[0xD9] = IIC_Read_One_Byte(0xA8, 0xD9);
	scdc_reg_data[0xDA] = IIC_Read_One_Byte(0xA8, 0xDA);
	scdc_reg_data[0xDB] = '\0';
	if (check){	
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xD3~0xDA] Device ID: %s\r\n", &scdc_reg_data[0xD3]);
		for (i = 0; i < 8; i++) {
			ccw_dbg_log_d(CCW_TAG, " 0x%02x", scdc_reg_data[i]);
		}
		ccw_dbg_log_d(CCW_TAG, "\r\n");
	}
	scdc_reg_data[0xDB] = IIC_Read_One_Byte(0xA8, 0xDB);
	if (check){
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDB 4H] Device ID, Hardware_Major_Rev: 0x%02x\r\n", scdc_reg_data[0xDB] >> 4);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDB 4L] Device ID, Hardware_Minor_Rev: 0x%02x\r\n", scdc_reg_data[0xDB] & 0x0F);
	}
	scdc_reg_data[0xDC] = IIC_Read_One_Byte(0xA8, 0xDC);
	scdc_reg_data[0xDD] = IIC_Read_One_Byte(0xA8, 0xDD);
	if (check){
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDC] Device ID, Software_Major_Rev: 0x%02x\r\n", scdc_reg_data[0xDC]);
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDD] Device ID, Software_Minor_Rev: 0x%02x\r\n", scdc_reg_data[0xDD]);
	}

	scdc_reg_data[0xDE] = IIC_Read_One_Byte(0xA8, 0xDE);
	scdc_reg_data[0xDF] = IIC_Read_One_Byte(0xA8, 0xDF);
	scdc_reg_data[0xF0] = IIC_Read_One_Byte(0xA8, 0xF0);
	scdc_reg_data[0xF1] = IIC_Read_One_Byte(0xA8, 0xF1);
	scdc_reg_data[0xF2] = IIC_Read_One_Byte(0xA8, 0xF2);
	scdc_reg_data[0xF3] = IIC_Read_One_Byte(0xA8, 0xF3);
	scdc_reg_data[0xF4] = IIC_Read_One_Byte(0xA8, 0xF4);
	scdc_reg_data[0xF5] = IIC_Read_One_Byte(0xA8, 0xF5);
	scdc_reg_data[0xF6] = IIC_Read_One_Byte(0xA8, 0xF6);
	scdc_reg_data[0xF7] = IIC_Read_One_Byte(0xA8, 0xF7);
	scdc_reg_data[0xF8] = IIC_Read_One_Byte(0xA8, 0xF8);
	scdc_reg_data[0xF9] = IIC_Read_One_Byte(0xA8, 0xF9);
	scdc_reg_data[0xFA] = IIC_Read_One_Byte(0xA8, 0xFA);
	scdc_reg_data[0xFB] = IIC_Read_One_Byte(0xA8, 0xFB);
	scdc_reg_data[0xFC] = IIC_Read_One_Byte(0xA8, 0xFC);
	scdc_reg_data[0xFD] = IIC_Read_One_Byte(0xA8, 0xFD);
	scdc_reg_data[0xFE] = IIC_Read_One_Byte(0xA8, 0xFE);
	scdc_reg_data[0xFF] = IIC_Read_One_Byte(0xA8, 0xFF);
	scdc_reg_data[0x100] = '\0';
	if (check) {
		ccw_dbg_log_d(CCW_TAG, "[SCDC][0xDE~0xFF] Manufacturer Specific: \r\n");
		for (i = 0; i < 18; i++) {
			ccw_dbg_log_d(CCW_TAG, "      [0x%02X]: 0x%02x\r\n", 0xDE + i, scdc_reg_data[0xDE + i]);
		}
		ccw_dbg_log_d(CCW_TAG, "\r\n");
		ccw_dbg_log_d(CCW_TAG, "-----------------------------------------------------------------------\r\n");

		for (i = 0; i < sizeof(need_check); i++){
			if (scdc_reg_data[need_check[i]] != 0x00){
				ret = scdc_reg_data[need_check[i]] != 0x00;
				ccw_dbg_log_d(CCW_TAG, "[SCDC][0x%02X] Error: 0x%02x != 0x%02x\r\n", need_check[i], scdc_reg_data[need_check[i]], 0x00);
			}
		}
		ccw_dbg_log_d(CCW_TAG, "-----------------------------------------------------------------------\r\n");
	}
    #endif
	return ret;
}

char scdc_test(void)
{
	int i = 0;
	
	ccw_dbg_log_d(CCW_TAG, "ready for SCDC Test:\r\n");
	for (i = 0; i< 100; i++)
		delay_ms(10);

	ccw_dbg_log_d(CCW_TAG, "Start SCDC Test:\r\n");
	HDMI_5V = 0;
	IIC_SCL = 0;
	IIC_SDA = 0;
	delay_ms(100);
	HDMI_5V = 1;
	IIC_SCL = 1;
	IIC_SDA = 1;
	while (!HDMI_HPD) {
		delay_ms(10);
	}
	show_scdc_data(0);
	
	ccw_dbg_log_d(CCW_TAG, "5V Pull Low\r\n");
	HDMI_5V = 0;
	IIC_SCL = 0;
	IIC_SDA = 0;
	for (i = 0; i < 100; i++){
		delay_ms(50);
	}
	HDMI_5V = 1;
	IIC_SCL = 1;
	IIC_SDA = 1;
	ccw_dbg_log_d(CCW_TAG, "5V Pull Hight\r\n");
	while (!HDMI_HPD) {
		delay_ms(10);
	}
	if (show_scdc_data(1)) {
		ccw_dbg_log_d(CCW_TAG, "5V uper Check Error!\r\n");
	}
	return 0;
}

void ccw_hdmi_connect(uint8_t enable)
{
	if (enable) {
		ENABLE_HDMI_SINK_STAT(HDMI_SINK_5V_STAT);
		HDMI_5V = 1;
		ccw_dbg_log_d(CCW_TAG, "Wait Sink HPD\r\n");
		while (!HDMI_HPD) {
			delay_ms(50);
		}
		ccw_dbg_log_d(CCW_TAG, "HPD: 1\r\n");
		ENABLE_HDMI_SINK_STAT(HDMI_SINK_HPD_STAT);
	}else {
		HDMI_5V = 0;
		DISABLE_HDMI_SINK_STAT(HDMI_SINK_5V_STAT|HDMI_SINK_HPD_STAT);
	}
}

void ccw_hdmi_read_edid()
{
	uint8_t edid_buffer[256] = {0};
	if (!IS_ENABLE_HDMI_SINK_STAT(HDMI_SINK_HPD_STAT)) {
		if (IS_ENABLE_HDMI_SINK_STAT(HDMI_SINK_5V_STAT)) {
			ccw_dbg_log_d(CCW_TAG, "[HDMI] Error: Sink HPD is Low\r\n");
			return ;
		}
		ccw_hdmi_connect(1);
		delay_ms(200);
	}
	ccw_dbg_log_d(CCW_TAG, "[HDMI] Sink, Read EDID\r\n");
	// read block 0
	if (IIC_Read_Bytes(0xA0, 0, 128, edid_buffer) == 128){
		ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID] Block.0:\r\n");
		ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID]       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
		for (int i = 0; i < 8; i++){
			ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID]  %02X: ", i << 4);
			for (int j = 0; j < 16; j++) {
				ccw_dbg_log_d(CCW_TAG, " %02x", edid_buffer[i * 16 + j]);
			}
			ccw_dbg_log_d(CCW_TAG, "\r\n");
		}
	}else {
		return ;
	}
	// read block 1
	if (IIC_Read_Bytes(0xA0, 128, 128, edid_buffer + 128) == 128){
		ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID] Block.1:\r\n");
		ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID]       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
		for (int i = 0; i < 8; i++){
			ccw_dbg_log_d(CCW_TAG, "[HDMI][EDID]  %02X: ", (i + 8) << 4);
			for (int j = 0; j < 16; j++) {
				ccw_dbg_log_d(CCW_TAG, " %02x", edid_buffer[128 + i * 16 + j]);
			}
			ccw_dbg_log_d(CCW_TAG, "\r\n");
		}
	}
}

void ccw_hdmi_init(void)
{
    // for HDMI GPIO Init
    // for (int i = 0; i < sizeof(gpioList) / sizeof(gpio_node_t); i++){
    //     RCC_APB2PeriphClockCmd(gpioList[i].RCC_APB2Periph, ENABLE);
    //     GPIO_Init(gpioList[i].GPIOx, &gpioList[i].GPIO_InitStruct);
    //     GPIO_WriteBit(gpioList[i].GPIOx, gpioList[i].GPIO_InitStruct.GPIO_Pin, gpioList[i].default_value);
    // }
	hdmi_sink_status = 0;

}

void ccw_hdmi_deinit(void)
{

}
